﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

using Uber.Collections;
using Uber.HabboHotel.Achievements;
using Uber.HabboHotel.Items;
using Uber.HabboHotel.Users.Badges;
using Uber.HabboHotel.Users.Subscriptions;
using Uber.HabboHotel.Users.Messenger;
using Uber.HabboHotel.Users.Inventory;
using Uber.HabboHotel.Rooms;
using Uber.HabboHotel.GameClients;
using Uber.Storage;
using Uber.HabboHotel.Quests;
using Uber.Messages;

namespace Uber.HabboHotel.Users
{
    class Habbo
    {
        public uint Id;

        public string Username;
        public string RealName;

        public string AuthTicket;

        public DateTime MutedTime;

        public DateTime UserConnected;

        public uint Rank;

        public string Motto;
        public string Look;
        public string Gender;

        public int Credits;
        public int ActivityPoints;
        public Double LastActivityPointsUpdate;

        public bool Muted;
        public bool IsBRB;

        public int Respect;
        public int RespectGiven;
        public int DailyRespectPoints;
        public int DailyPetRespectPoints;

        public uint LoadingRoom;
        public Boolean LoadingChecksPassed;
        public uint CurrentRoomId;
        public uint HomeRoom;

        public bool IsTeleporting;
        public uint TeleporterId;
        public uint CurrentQuestId;
        public int CurrentQuestProgress;

        public SafeList<uint> FavoriteRooms;
        public List<uint> MutedUsers;
        public SafeList<string> Tags;
        public Dictionary<uint, int> Achievements;
        public List<uint> CompletedQuests;
        public List<uint> RatedRooms;
        public List<WardRobe> Wardrobe;


        public int TimeOnline;

        #region AchievementsValues
        public DateTime LastRespectUpdate;
        #endregion

        private SubscriptionManager SubscriptionManager;
        private HabboMessenger Messenger;
        private BadgeComponent BadgeComponent;
        private InventoryComponent InventoryComponent;
        private AvatarEffectsInventoryComponent AvatarEffectsInventoryComponent;
        private Dictionary<string, int> InFractions;

        public int NewbieStatus;
        public bool SpectatorMode;
        public bool Disconnected;
        public Boolean HasRoomEnterd;

        public bool CalledGuideBot;
        public bool MutantPenalty;

        public int LevelBuilder;
        public int LevelSocial;
        public int LevelIdentity;
        public int LevelExplorer;

        public int MarketplaceTickets;

        public uint LastQuestId;

        private readonly Object _lock;
        
        public bool BlockNewFriends;
        public Boolean IsUltraVip;
        public int FireworkCount;

        public Boolean InRoom
        {
            get
            {
                return (CurrentRoomId >= 1);
            }
        }

        public Room CurrentRoom
        {
            get
            {
                if (CurrentRoomId <= 0)
                {
                    return null;
                }

                return UberEnvironment.GetGame().GetRoomManager().GetRoom(CurrentRoomId);
            }
        }

        public Boolean NeedsRespectUpdate
        {
            get
            {
                if (this.LastRespectUpdate == null)
                {
                    return true;
                }

                TimeSpan Diff = (DateTime.Now - this.LastRespectUpdate);
                Boolean NeedsUpdate = false;

                if (IsUltraVip && Diff.TotalHours >= 2) { NeedsUpdate = true; }
                if (!IsUltraVip && Diff.TotalDays == 1) { NeedsUpdate = true; }


                return (NeedsUpdate && (this.DailyPetRespectPoints < 3 || this.DailyRespectPoints < 3));
            }
        }

        public Habbo(uint mId, string mUsername, string mRealName, string mAuthTicket, uint mRank, string mMotto, string mLook, 
            string mGender, int mCredits, int mActivityPoints, Double mLastActivityPointsUpdate, bool mMuted, uint mHomeRoom, 
            int mRespect, int mDailyRespectPoints, int mDailyPetRespectPoints, int mNewbieStatus, bool mMutantPenalty, bool mBlockNewFriends, 
            uint mCurrentQuestId, int mCurrentQuestProgress, int mLevelBuilder, int mLevelSocial, int mLevelIdentity, int mLevelExplorer, 
            int mRespectGiven, string mLastRespectUpdate, int mTimeOnline, Boolean mIsUltraVip,int mMacketplaceTickets, int mFireworkCount)
        {
            this.Id = mId;
            this.Username = mUsername;
            this.RealName = mRealName;
            this.AuthTicket = mAuthTicket;
            this.Rank = mRank;
            this.Motto = mMotto;
            this.Look = mLook.ToLower();
            this.Gender = mGender.ToLower();
            this.Credits = mCredits;
            this.ActivityPoints = mActivityPoints;
            this.LastActivityPointsUpdate = mLastActivityPointsUpdate;
            this.Muted = mMuted;

            this.MutedTime = new DateTime();

            if (mLastRespectUpdate != "")
            {
                this.LastRespectUpdate = DateTime.Parse(mLastRespectUpdate);
            }

            this.LoadingRoom = 0;
            this.LoadingChecksPassed = false;
            this.CurrentRoomId = 0;
            this.HomeRoom = mHomeRoom;

            this.FavoriteRooms = new SafeList<uint>();
            this.MutedUsers = new List<uint>();
            this.Tags = new SafeList<string>();
            this.Achievements = new Dictionary<uint, int>();
            this.RatedRooms = new List<uint>();
            this.CompletedQuests = new List<uint>();
            this.InFractions = new Dictionary<string, int>();
            this.Wardrobe = new List<WardRobe>();

            this.Respect = mRespect;
            this.DailyRespectPoints = mDailyRespectPoints;
            this.DailyPetRespectPoints = mDailyPetRespectPoints;
            this.RespectGiven = mRespectGiven;

            this.NewbieStatus = mNewbieStatus;
            this.CalledGuideBot = false;
            this.MutantPenalty = mMutantPenalty;
            this.BlockNewFriends = mBlockNewFriends;

            this.IsTeleporting = false;
            this.HasRoomEnterd = false;
            this.TeleporterId = 0;

            this.MarketplaceTickets = mMacketplaceTickets;

            this.SubscriptionManager = new SubscriptionManager(Id);
            this.BadgeComponent = new BadgeComponent(Id);
            this.InventoryComponent = new InventoryComponent(Id);
            this.AvatarEffectsInventoryComponent = new AvatarEffectsInventoryComponent(Id);

            this.SpectatorMode = false;
            this.Disconnected = false;

            this.LastQuestId = 0;
            this.CurrentQuestId = mCurrentQuestId;
            this.CurrentQuestProgress = mCurrentQuestProgress;

            this.LevelBuilder = mLevelBuilder;
            this.LevelIdentity = mLevelIdentity;
            this.LevelSocial = mLevelSocial;
            this.LevelExplorer = mLevelExplorer;

            this.FireworkCount = mFireworkCount;

            UberEnvironment.GetLogging().WriteLine("User -> " + Username + " connected.", Core.LogLevel.Debug);

            this.UserConnected = DateTime.Now;
            this.TimeOnline = mTimeOnline;

            this.IsBRB = false;

            this._lock = new Object();
            this.IsUltraVip = mIsUltraVip;

            if (!IsUltraVip && Rank >= 3)
            {
                IsUltraVip = true;
            }
        }

        #region Loading
        public void LoadData()
        {
            lock (_lock)
            {
                this.SubscriptionManager.LoadSubscriptions();
                this.BadgeComponent.LoadBadges();
                this.InventoryComponent.LoadInventory();
                this.AvatarEffectsInventoryComponent.LoadEffects();

                this.LoadAchievements();
                this.LoadFavorites();
                this.LoadMutedUsers();
                this.LoadTags();
                this.LoadQuests();
                this.LoadWardRobe();
            }

            if (this.CurrentQuestId != 0)
            {
                Quest Quest = UberEnvironment.GetGame().GetQuestManager().GetQuest(this.CurrentQuestId);
                ServerMessage Message = new ServerMessage(802);

                Quest.Serialize(Message, GetClient(),true);

                GetClient().SendMessage(Message);
            }

            if (NeedsRespectUpdate)
            {
                this.DailyRespectPoints = 3;
                this.DailyPetRespectPoints = 3;

                using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
                {
                    dbClient.AddParamWithValue("userid", Id);
                    dbClient.ExecuteQuery("UPDATE users SET daily_respect_points = 3,daily_pet_respect_points = 3,last_respect_update = '" + DateTime.Now + "' WHERE id = @userid LIMIT 1");
                }
            }
        }

        public void LoadQuests()
        {
            this.CompletedQuests = null;
            this.CompletedQuests = new List<uint>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT quest_id FROM user_quests WHERE user_id = '" + Id + "'");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                CompletedQuests.Add((UInt32)Row["quest_Id"]);
            }
        }

        public void LoadFavorites()
        {
            this.FavoriteRooms = null;
            this.FavoriteRooms = new SafeList<uint>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT room_id FROM user_favorites WHERE user_id = '" + Id + "'");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                FavoriteRooms.Add((uint)Row["room_id"]);
            }
        }

        public void LoadMutedUsers()
        {
            this.MutedUsers = null;
            this.MutedUsers = new List<uint>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT ignore_id FROM user_ignores WHERE user_id = '" + Id + "'");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                MutedUsers.Add((uint)Row["ignore_id"]);
            }
        }

        public void LoadTags()
        {
            this.Tags = null;
            this.Tags = new SafeList<string>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT tag FROM user_tags WHERE user_id = '" + Id + "'");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                Tags.Add((string)Row["tag"]);
            }

            if (Tags.Count >= 5)
            {
                UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 7, 1);
            }
        }

        public void CheckPetCount()
        {
            int Count = 0;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Count = dbClient.ReadInt32("SELECT COUNT(*) FROM user_pets WHERE user_id = '" + Id + "'");
            }

            if (Count <= 0)
            {
                return;
            }

            if (Count >= 1) { GetClient().UnlockAchievement(20, 1); }
            if (Count >= 5) { GetClient().UnlockAchievement(20, 2); }
            if (Count >= 10) { GetClient().UnlockAchievement(20, 3); }
            if (Count >= 15) { GetClient().UnlockAchievement(20, 4); }
            if (Count >= 20) { GetClient().UnlockAchievement(20, 5); }

            if (Count >= 25) { GetClient().UnlockAchievement(20, 6); }
            if (Count >= 30) { GetClient().UnlockAchievement(20, 7); }
            if (Count >= 40) { GetClient().UnlockAchievement(20, 8); }
            if (Count >= 50) { GetClient().UnlockAchievement(20, 9); }
            if (Count >= 75) { GetClient().UnlockAchievement(20, 10); }
        }

        public void CheckFireworkAchievements()
        {
            int Count = FireworkCount;

            if (Count <= 0)
            {
                return;
            }

            if (Count >= 10) { GetClient().UnlockAchievement(21, 1); }
            if (Count >= 50) { GetClient().UnlockAchievement(21, 2); }
            if (Count >= 100) { GetClient().UnlockAchievement(1, 3); }
            if (Count >= 150) { GetClient().UnlockAchievement(21, 4); }
            if (Count >= 200) { GetClient().UnlockAchievement(21, 5); }

            if (Count >= 250) { GetClient().UnlockAchievement(21, 6); }
            if (Count >= 300) { GetClient().UnlockAchievement(21, 7); }
            if (Count >= 400) { GetClient().UnlockAchievement(21, 8); }
            if (Count >= 500) { GetClient().UnlockAchievement(21, 9); }
            if (Count >= 750) { GetClient().UnlockAchievement(21, 10); }
        }

        public void LoadAchievements()
        {
            this.Achievements = null;
            this.Achievements = new Dictionary<uint, int>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT achievement_id,achievement_level FROM user_achievements WHERE user_id = '" + Id + "'");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                Achievements.Add((uint)Row["achievement_id"], (int)Row["achievement_level"]);
            }
        }

        public void LoadWardRobe()
        {
            this.Wardrobe = null;
            this.Wardrobe = new List<WardRobe>();

            DataTable Data = null;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                Data = dbClient.ReadDataTable("SELECT slot_id,look,gender FROM user_wardrobe WHERE user_id = '" + Id + "' ORDER by slot_id DESC");
            }

            if (Data == null)
            {
                return;
            }

            foreach (DataRow Row in Data.Rows)
            {
                Wardrobe.Add(new WardRobe((uint)Row["slot_id"], (string)Row["look"], (string)Row["gender"]));
            }
        }

        #endregion

        public int GetAchievementScore()
        {
            double score = 0;

            foreach (UInt32 Id in this.Achievements.Keys)
            {
                Achievement Achievement = UberEnvironment.GetGame().GetAchievementManager().Achievements[Id];
                double multipler = Achievement.PixelMultiplier + 1;
                score += Achievement.PixelBase * multipler;
            }

            return (int)score;
        }

        public WardRobe GetWardRobeBySlot(uint SlotId)
        {
            foreach (WardRobe Robe in this.Wardrobe)
            {
                if (Robe.SlotId == SlotId)
                {
                    return Robe;
                }
            }

            return null;
        }

        public void CheckOnlineAchievements()
        {
            int i = this.GetTotalMinutes;

            if (i <= 0)
            {
                return;
            }

            if (i >= 60) { GetClient().UnlockAchievement(19, 1); }
            if (i >= 180) { GetClient().UnlockAchievement(19, 2); }
            if (i >= 480) { GetClient().UnlockAchievement(19, 3); }
            if (i >= 960) { GetClient().UnlockAchievement(19, 4); }
            if (i >= 2880) { GetClient().UnlockAchievement(19, 5); }

            if (i >= 8640) { GetClient().UnlockAchievement(19, 6); }
            if (i >= 17280) { GetClient().UnlockAchievement(19, 7); }
            if (i >= 34560) { GetClient().UnlockAchievement(19, 8); }
            if (i >= 69120) { GetClient().UnlockAchievement(19, 9); }
            if (i >= 138240) { GetClient().UnlockAchievement(19, 10); }
        }

        #region Ontime
        public void OnDisconnect()
        {
            if (this.Disconnected)
            {
                return;
            }

            this.Disconnected = true;

            UberEnvironment.GetLogging().WriteLine("User <- " + Username + " disposed.", Core.LogLevel.Debug);

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                dbClient.ExecuteQuery("UPDATE users SET last_online = '" +  DateTime.Now + "', online = '0',time_online = time_online + " + this.GetSessionMinutes + " WHERE id = '" + Id + "' LIMIT 1");
            }

            if (InRoom)
            {

                if (GetClient() != null)
                {
                    UberEnvironment.GetGame().GetRoomManager().GetRoom(CurrentRoomId).RemoveUserFromRoom(GetClient(), false, false);
                }
            }

            if (Messenger != null)
            {
                Messenger.AppearOffline = true;
                Messenger.OnStatusChanged(true);
            }

            if (SubscriptionManager != null)
            {
                SubscriptionManager.Clear();
            }

            this.FavoriteRooms = null;
            this.CompletedQuests = null;
            this.Achievements = null;
            this.MutedUsers = null;
            this.Tags = null;
        }

        public void OnEnterRoom(uint RoomId)
        {
            int Rm_Visits = 0;

            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                dbClient.ExecuteQuery("INSERT INTO user_roomvisits (user_id,room_id,entry_timestamp,exit_timestamp,hour,minute) VALUES ('" + Id + "','" + RoomId + "','" + UberEnvironment.GetUnixTimestamp() + "','0','" + DateTime.Now.Hour + "','" + DateTime.Now.Minute + "')");
                Rm_Visits = dbClient.ReadInt32("SELECT COUNT(*) FROM user_roomvisits WHERE user_id = '" + Id + "'");
            }

            this.CurrentRoomId = RoomId;

            if (CurrentRoom.Owner != Username && !CurrentRoom.IsPublic)
            {
                if (CurrentQuestId == 15)
                {
                    UberEnvironment.GetGame().GetQuestManager().UpdateQuest(15, GetClient());
                }
            }

            if (Rm_Visits > 0)
            {
                if (Rm_Visits >= 5) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 1); }
                if (Rm_Visits >= 15) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 2); }
                if (Rm_Visits >= 30) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 3); }
                if (Rm_Visits >= 50) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 4); }
                if (Rm_Visits >= 60) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 5); }
                if (Rm_Visits >= 80) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 6); }
                if (Rm_Visits >= 120) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 7); }
                if (Rm_Visits >= 140) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 8); }
                if (Rm_Visits >= 160) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 9); }
                if (Rm_Visits >= 200) { UberEnvironment.GetGame().GetAchievementManager().UnlockAchievement(GetClient(), 12, 10); }
            }

            Messenger.OnStatusChanged(false);
        }

        public void OnLeaveRoom()
        {
            using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
            {
                dbClient.ExecuteQuery("UPDATE user_roomvisits SET exit_timestamp = '" + UberEnvironment.GetUnixTimestamp() + "' WHERE room_id = '" + this.CurrentRoomId + "' AND user_id = '" + Id + "' ORDER BY entry_timestamp DESC LIMIT 1");
            }

            this.CurrentRoomId = 0;

            if (Messenger != null)
            {
                Messenger.OnStatusChanged(false);
            }
        }

        #endregion

        public void InitMessenger()
        {
            if (GetMessenger() != null)
            {
                return;
            }

            Messenger = new HabboMessenger(Id);

            Messenger.LoadBuddies();
            Messenger.LoadRequests();

            GetClient().SendMessage(Messenger.SerializeFriends());
            GetClient().SendMessage(Messenger.SerializeRequests());

            Messenger.OnStatusChanged(true);
        }

        #region Values
        public void UpdateCreditsBalance(Boolean InDatabase)
        {
            GetClient().GetMessageHandler().GetResponse().Init(6);
            GetClient().GetMessageHandler().GetResponse().AppendStringWithBreak(Credits + ".0");
            GetClient().GetMessageHandler().SendResponse();

            if (InDatabase)
            {
                using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
                {
                    dbClient.ExecuteQuery("UPDATE users SET credits = '" + Credits + "' WHERE id = '" + Id + "' LIMIT 1");
                }
            }
        }

        public void UpdateActivityPointsBalance(Boolean InDatabase)
        {
            UpdateActivityPointsBalance(InDatabase, 0);
        }

        public void UpdateActivityPointsBalance(Boolean InDatabase, int NotifAmount)
        {
            GetClient().GetMessageHandler().GetResponse().Init(438);
            GetClient().GetMessageHandler().GetResponse().AppendInt32(ActivityPoints);
            GetClient().GetMessageHandler().GetResponse().AppendInt32(NotifAmount);
            GetClient().GetMessageHandler().SendResponse();

            if (InDatabase)
            {
                using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient())
                {
                    dbClient.ExecuteQuery("UPDATE users SET activity_points = '" + ActivityPoints + "', activity_points_lastupdate = '" + LastActivityPointsUpdate + "' WHERE id = '" + Id + "' LIMIT 1");
                }
            }
        }
        #endregion

        #region Chat
        public void Mute()
        {
            if (!this.Muted)
            {
                GetClient().SendNotif("You have been muted by an moderator.");
                this.Muted = true;
            }
        }

        public void Unmute()
        {
            if (this.Muted)
            {
                GetClient().SendNotif("You have been unmuted by an moderator.");
                this.Muted = false;
            }
        }

        public void HandleInfraction(string InFraction)
        {
            if (this.InFractions.ContainsKey(InFraction))
            {
                this.InFractions[InFraction]++;
            }
            else
            {
                this.InFractions[InFraction] = 1;
            }

            if (this.InFractions[InFraction] >= 5)
            {
                this.MutedTime = DateTime.Now;
                this.InFractions[InFraction] = 0;
                this.GetClient().SendNotif("You have been muted by scolding.");
            }
        }

        public Boolean ExpiredBan
        {
            get
            {
                if (this.MutedTime == null)
                {
                    return true;
                }

                return ((DateTime.Now - this.MutedTime).TotalMinutes >= 5);
            }
        }
        #endregion

        public bool HasFuse(string Fuse)
        {
            if (UberEnvironment.GetGame().GetRoleManager().RankHasRight(Rank, Fuse))
            {
                return true;
            }

            foreach (string SubscriptionId in GetSubscriptionManager().SubList)
            {
                if (UberEnvironment.GetGame().GetRoleManager().SubHasRight(SubscriptionId, Fuse))
                {
                    return true;
                }
            }

            return false;
        }

        public int GetSessionMinutes
        {
            get
            {
                return (int)(DateTime.Now - this.UserConnected).TotalMinutes;
            }
        }

        public int GetTotalMinutes
        {
            get
            {
                return (this.TimeOnline + this.GetSessionMinutes);
            }
        }

        private GameClient GetClient()
        {
            return UberEnvironment.GetGame().GetClientManager().GetClientByHabbo(Id);
        }

        public SubscriptionManager GetSubscriptionManager()
        {
            return SubscriptionManager;
        }

        public HabboMessenger GetMessenger()
        {
            return Messenger;
        }

        public BadgeComponent GetBadgeComponent()
        {
            return BadgeComponent;
        }

        public InventoryComponent GetInventoryComponent()
        {
            return InventoryComponent;
        }

        public AvatarEffectsInventoryComponent GetAvatarEffectsInventoryComponent()
        {
            return AvatarEffectsInventoryComponent;
        }
    }

    class WardRobe
    {
        public uint SlotId;
        public string Look;
        public string Gender;

        public WardRobe(uint mSlotId, string mLook, string mGender)
        {
            this.SlotId = mSlotId;
            this.Look = mLook.ToLower();
            this.Gender = mGender.ToUpper();
        }
    }
}
